/************************************************************************

  Heroes IV
  Copyright 2000, The 3DO Company
  
	------------------------------------------------------------------
	bitmap_layer.h
	
	  $Header: /resource_editor/bitmap_layer.h $
	  
		$NoKeywords: $
		
************************************************************************/
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "battlefield_preset_map.h"
#include "owned_ptr.h"

char const t_resource_traits<t_battlefield_preset_map>::prefix[] = "battlefield_preset_map";

char const t_resource_traits<t_battlefield_preset_map>::extension[] = "bpm";

namespace {
	
	unsigned short k_bitmap_24_version = 1;
};

//t_battlefield_preset_map class loads and saves raw 24bit bitmaps.
//namespace 
//{
	const int k_version = 1;
	
	class t_alpha_iterator
	{
	public:
		
		t_alpha_iterator( t_bitmap_layer_24 const & bitmap_24, t_screen_rect	max_event );
		
		~t_alpha_iterator();
		
		unsigned char get_pixel_alpha(int x, int y);
		
		bool		  is_iso_cell_alpha(int x, int y, int iso_cell_height);
		bool		  is_iso_cell_invisible(int x, int y, int iso_cell_height);
		
	protected:
		
		bool		check_iso_cell(int x, int y, int iso_cell_height, bool (*alpha_check)(unsigned char alpha) );
		
		static bool		check_iso_cell_invisible( unsigned char alpha );
		static bool		check_iso_cell_alpha( unsigned char alpha );
		
		void		clip_screen_point( t_screen_point & point );
		
		t_owned_array<unsigned char>	m_alpha_array;
		
		int m_height;
		int m_width;
	};
	
	t_alpha_iterator::t_alpha_iterator( t_bitmap_layer_24 const & bitmap_24, t_screen_rect	max_extent )
	{
		t_uint8*						bitmap_24_data_ptr	= bitmap_24.get_data();
		t_uint8*						alpha_data_ptr		= bitmap_24.get_alpha_mask();
		t_bitmap_row const*				bitmap_row			= NULL;
		int								alpha_depth			= bitmap_24.get_alpha_depth();
		int								height				= bitmap_24.get_height();
		int								width				= bitmap_24.get_width();
		t_screen_rect					extent_rect			= bitmap_24.get_rect();
		
		int								max_width			= max_extent.right -	max_extent.left;
		int								max_height			= max_extent.bottom -	max_extent.top;
		int								dy					= extent_rect.top -		max_extent.top ;
		int								dx					= extent_rect.left -	max_extent.left ;
		
		m_width		= max_width;
		m_height	= max_height;
		
		/////////////////////////////////////////////////////////////////////////////////////////
		
		m_alpha_array.reset(new unsigned char[m_width*m_height]);
		memset(m_alpha_array.get(),0,m_width*m_height);
		
		/////////////////////////////////////////////////////////////////////////////////////////
		
		int				ndx_24				= 0;
		int				ndx_alpha			= 0;
		int				current_row			= 0;
		int				current_alpha_row   = 0;
		int				row_size			= 0;
		int				pixel_24			= 0;
		
		while (current_row < height)
		{
			//handle the bitmap_row
			
			current_alpha_row = current_row + dy ;
			
			bitmap_row = bitmap_24.get_row(current_row);
			
			assert(bitmap_row != NULL);
			
			row_size = bitmap_row->right - bitmap_row->left;
			
			ndx_24 = bitmap_row->offset;
			
			ndx_alpha = (current_alpha_row*m_width) + bitmap_row->left + dx;
			
			//find the pixel at the location ndx in the bitmap 24
			
			for (int i = 0; i < row_size; i++)
			{
				//find the bitmap 24 pixel (RGB) value using the palette
				//find the alpha using the alpha mask
				
				pixel_24 = bitmap_24_data_ptr[ndx_24];
				
				switch (alpha_depth)
				{
				case 1:
					
					if (pixel_24 == 0)
						m_alpha_array[ndx_alpha]	= 0;
					else
						m_alpha_array[ndx_alpha]	= 255;
					
					break;
					
				case 4:
					
					//the bitmap 32 expects alpha in the value of 0x10 <= value < 0xF0
					
					if ((ndx_24 & 1) == 0)	
						m_alpha_array[ndx_alpha]	= (alpha_data_ptr[ndx_24/2] & 0x0F) << 4 ;
					else
						m_alpha_array[ndx_alpha]	= (alpha_data_ptr[ndx_24/2] & 0xF0);
					
					break;
					
				case 0 : default:
					
					m_alpha_array[ndx_alpha]	= 255;
					
					break;
				}
				
				ndx_24++;
				ndx_alpha++;
			}
			
			current_row++;
		}
	}
	
	//////////////////////////////////////////////////////////////////////////////////////////
	t_alpha_iterator::~t_alpha_iterator()
	{
		m_alpha_array.reset(NULL);
	}


	//////////////////////////////////////////////////////////////////////////////////////////		
	bool		t_alpha_iterator::check_iso_cell_invisible( unsigned char alpha )
	{
		if (alpha != 0) 
			return false;
		
		return true;
	}


	//////////////////////////////////////////////////////////////////////////////////////////
	bool		t_alpha_iterator::check_iso_cell_alpha( unsigned char alpha )
	{
		if (alpha == 255)
			return false;
		
		return true;
	}


	//////////////////////////////////////////////////////////////////////////////////////////
	void t_alpha_iterator::clip_screen_point( t_screen_point & point )
	{
		if (point.x < 0) 
			point.x = 0;
		
		if (point.x >= m_width)
			point.x  = m_width-1;
		
		if (point.y < 0)
			point.y = 0;
		
		if (point.y >= m_height) 
			point.y  = m_height-1;
	}


	//////////////////////////////////////////////////////////////////////////////////////////
	unsigned char t_alpha_iterator::get_pixel_alpha(int x, int y)
	{
		if (m_alpha_array.get() == NULL) 
			return 0;
		
		if (x < 0 || x >= m_width || y < 0 || y >= m_height) 
			return 0;
		
		return m_alpha_array[m_width*y + x];
	}

	//////////////////////////////////////////////////////////////////////////////////////////
	bool t_alpha_iterator::is_iso_cell_alpha(int x, int y, int iso_cell_height)
	{
		return check_iso_cell( x, y, iso_cell_height, check_iso_cell_alpha );
	}

	//////////////////////////////////////////////////////////////////////////////////////////
	bool t_alpha_iterator::is_iso_cell_invisible(int x, int y, int iso_cell_height)
	{
		return check_iso_cell( x, y, iso_cell_height, check_iso_cell_invisible );
	}

	//////////////////////////////////////////////////////////////////////////////////////////
	bool t_alpha_iterator::check_iso_cell(int x, int y, int iso_cell_height, bool (*alpha_check)(unsigned char alpha) )
	{
		int iso_cell_width = iso_cell_height*2;
		
		t_screen_point begin;
		t_screen_point end;

		t_screen_point clip_begin;
		t_screen_point clip_end;

		begin.x = x-1;
		begin.y = y;
		
		end.x = x+1;
		end.y = y;
			
		int half_height = iso_cell_height/2;
		int stop_y		= begin.y+half_height;
		
		for (int cell_top = begin.y; cell_top < stop_y; cell_top++)
		{
			clip_begin	= begin;
			clip_end	= end;

			clip_screen_point(clip_begin);
			clip_screen_point(clip_end);

			int pixel_y = clip_begin.y;
			
			for (int pixel_x = clip_begin.x; pixel_x <= clip_end.x; pixel_x++)
			{
				if ( alpha_check(m_alpha_array[m_width*pixel_y + pixel_x]))
					return true;
			}
			
			begin.x = begin.x - 2;
			begin.y = begin.y + 1;
			
			end.x = end.x + 2;
			end.y = end.y + 1;
		}
		
		begin.x = begin.x + 1;
		begin.y = begin.y + 1;
		
		end.x = end.x - 1;
		end.y = end.y + 1;

		stop_y		+= half_height;

		for ( int cell_bottom = begin.y; cell_bottom < stop_y; cell_bottom++)
		{
			clip_begin	= begin;
			clip_end	= end;

			clip_screen_point(clip_begin);
			clip_screen_point(clip_end);
			
			int pixel_y = clip_begin.y;

			for (int pixel_x = clip_begin.x; pixel_x <= clip_end.x; pixel_x++)
			{
				if ( alpha_check(m_alpha_array[m_width*pixel_y + pixel_x]))
					return true;
			}
			
			begin.x = begin.x + 2;
			begin.y = begin.y + 1;
			
			end.x = begin.x - 2;
			end.y = begin.y + 1;
		}
		
		return false;
	}
//};


///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////


// Constructors / Destructor
t_battlefield_preset_map::t_battlefield_preset_map()
{
	m_passability_map = new t_battlefield_passablity_map();
}

t_battlefield_preset_map::~t_battlefield_preset_map()
{
	
}

void t_battlefield_preset_map::initialize_passability_map(
														  int						map_size,
														  int						tile_height,
														  t_screen_point const &	view_size
														  )
{
	m_passability_map = NULL;
	
	m_passability_map = new t_battlefield_passablity_map(map_size,tile_height,view_size);
}

void t_battlefield_preset_map::initialize_backdrop( t_bitmap_layer_24 const &		backdrop_source )
{
	m_backdrop_group.resize(0);
	m_backdrop_group.push_back(backdrop_source);
}

void t_battlefield_preset_map::calculate_passability_map( t_bitmap_layer_24 const & bitmap, t_bitmap_layer_24 const & mask ,
														 t_bitmap_layer_24 const & obstacle, t_screen_rect	max_extent )
{
	//using the alpha mask and bitmap calcuate the passability map.
	
	t_alpha_iterator alpha_map_bitmap( bitmap, max_extent );
	t_alpha_iterator alpha_map_mask( mask, max_extent );
	t_alpha_iterator alpha_map_obstacle( obstacle, max_extent );


	//iterate through the passability map and query the alpha maps to determine the passability state
	//of that cell.
	
	t_map_point_2d					point;
	int								row_end;
	int								tile_height			= m_passability_map->get_tile_height();
	int								half_tile_height	= tile_height/2;
	
	point.row = m_passability_map->get_size();
	
	while (point.row--)
	{
		point.column	= m_passability_map->get_row_start( point.row );
		row_end			= m_passability_map->get_row_end( point.row );
		
		while (point.column < row_end)
		{	
			t_int8 & element	=  m_passability_map->get( point );
			t_screen_point screen_point				=  m_passability_map->get_screen_point(point);
			t_screen_point middle_point				=  screen_point;
			
			middle_point.y							+= half_tile_height;

			//the various states are defined as :
			//	-undefined	= a completely invisible tile on the bitmap
			//	-impassable = an alpha within the alpha mask will make the tile impassable
			//	-passable	= a completely opaque tile within the alpha mask.

			//bitmap defines the undefined

			//early out check, see if the middle has any alpha.

			if (alpha_map_bitmap.get_pixel_alpha(middle_point.x, middle_point.y) != 0)
			{
				//the mask defines passability.
				
				if (alpha_map_mask.get_pixel_alpha(middle_point.x, middle_point.y) != 255)
				{
					element = k_combat_passability_impassable;
					point.column++;
					continue;
				}
			}
			else
			{	
				//check for possible undefiness, needs complete check

				if (alpha_map_bitmap.is_iso_cell_invisible(screen_point.x, screen_point.y, tile_height) == true)
				{
					element = k_combat_passability_undefined;
					point.column++;
					continue;
				}
			}	
			
			//to set passability we have to query the entire isometric cells pixel
			
			if (alpha_map_mask.is_iso_cell_alpha(screen_point.x, screen_point.y, tile_height) == true)
				element = k_combat_passability_impassable;
			else
			{
				element = k_combat_passability_passable;
				
				if (alpha_map_obstacle.get_pixel_alpha(middle_point.x, middle_point.y) != 0)
				{
					element = k_combat_passability_passable_no_obstacle;
				}
			}
			
			point.column++;
		}
	}
}

bool	t_battlefield_preset_map::read( std::streambuf& buffer)
{
	int version = get<int>(buffer);
	
	m_passability_map->read(buffer, version);
	m_backdrop_group.read(buffer, NULL);
	
	return true;
}

bool	t_battlefield_preset_map::write( std::streambuf& buffer ) const
{
	put<int>(buffer,k_version);
	
	m_passability_map->write(buffer);
	m_backdrop_group.write(buffer);
	
	return true;
}
